package com.iwjw.ibatis.mapper;

import com.iwjw.ibatis.criteria.Query;
import com.iwjw.ibatis.entity.ColumnInfo;
import com.iwjw.ibatis.entity.TableInfo;
import com.iwjw.ibatis.exception.IWMybatisException;
import com.iwjw.ibatis.session.MybatisConfiguration;
import com.iwjw.ibatis.util.MysqlType2JdbcType;
import com.iwjw.ibatis.util.TableInfoUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.ResultMapResolver;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author weiwei.huang
 * @Time 2016/10/14
 */
public class AutoSqlInjector implements ISqlInjector {

    private static final String WHERE_CLAUSE = "\n" +
            "<where >\n" +
            "  <foreach collection=\"%soredCriteria\" item=\"criteria\" separator=\"or\" >\n" +
            "    <if test=\"criteria.valid\" >\n" +
            "      <trim prefix=\"(\" suffix=\")\" prefixOverrides=\"and\" >\n" +
            "        <foreach collection=\"criteria.criteria\" item=\"criterion\" >\n" +
            "          <choose >\n" +
            "            <when test=\"criterion.noValue\" >\n" +
            "              and ${criterion.condition}\n" +
            "            </when>\n" +
            "            <when test=\"criterion.singleValue\" >\n" +
            "              and ${criterion.condition} #{criterion.value}\n" +
            "            </when>\n" +
            "            <when test=\"criterion.betweenValue\" >\n" +
            "              and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
            "            </when>\n" +
            "            <when test=\"criterion.listValue\" >\n" +
            "              and ${criterion.condition}\n" +
            "              <foreach collection=\"criterion.value\" item=\"listItem\" open=\"(\" close=\")\" separator=\",\" >\n" +
            "                #{listItem}\n" +
            "              </foreach>\n" +
            "            </when>\n" +
            "          </choose>\n" +
            "        </foreach>\n" +
            "      </trim>\n" +
            "    </if>\n" +
            "  </foreach>\n" +
            "</where>\n";

    public static String DISTINCT_TEMPLATE = "" +
            "<if test=\"%sdistinct\" >\n" +
            "    distinct\n" +
            "</if>" +
            "%s";


    public static String ORDER_TEMPLATE = "" +
            "<if test=\"%sorderByClause\" >\n" +
            "    order by ${%sorderByClause}\n" +
            "</if>";

    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    protected Configuration configuration;
    protected LanguageDriver languageDriver;
    protected MapperBuilderAssistant builderAssistant;

    public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        String className = mapperClass.getName();
        if (MybatisConfiguration.MAPPER_REGISTRY_CACHE.contains(className)) {
            return;
        }
        this.configuration = configuration;
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanuageInstance();
        Class<?> modelClass = extractModelClass(mapperClass);
        TableInfo tableInfo = null;
        try {
            tableInfo = TableInfoUtil.init(modelClass);
        } catch (IWMybatisException e) {
            e.printStackTrace();
        }
        if (null != tableInfo) {
            /* 插入 */
            this.injectInsert(true, false, mapperClass, modelClass, tableInfo); //批量插入
            this.injectInsert(false, false, mapperClass, modelClass, tableInfo);//单条插入
            this.injectInsert(false, true, mapperClass, modelClass, tableInfo); //单条selective插入

            /* 删 */
            this.injectDeleteByPrimaryKey(false, mapperClass, tableInfo); //主键删除
            this.injectDeleteByPrimaryKey(true, mapperClass, tableInfo); //主键批量删除

            this.injectDeleteByQuery(mapperClass, tableInfo);  //根据查询语句查出

            /* 改 */
            this.injectUpdateByPrimaryKey(true, true, mapperClass, modelClass, tableInfo); //乐观锁 selective更新
            this.injectUpdateByPrimaryKey(false, false, mapperClass, modelClass, tableInfo);//更新
            this.injectUpdateByPrimaryKey(true, false, mapperClass, modelClass, tableInfo); //乐观锁 更新
            this.injectUpdateByPrimaryKey(false, true, mapperClass, modelClass, tableInfo); //非乐观锁 selective更新

            this.injectUpdateByQuery(false, mapperClass, modelClass, tableInfo);  //通过查询条件更新
            this.injectUpdateByQuery(true, mapperClass, modelClass, tableInfo);   //通过查询条件更新 selective更新

            /* 查 */
            this.injectSelectByPrimaryKey(true, true, mapperClass, modelClass, tableInfo); //指定字段通过主键批量查询
            this.injectSelectByPrimaryKey(true, false, mapperClass, modelClass, tableInfo); //指定字段主键查询

            this.injectSelectByPrimaryKey(false, true, mapperClass, modelClass, tableInfo);//通过主键批量查询
            this.injectSelectByPrimaryKey(false, false, mapperClass, modelClass, tableInfo);//主键查询


            this.injectSelectByQuery(true, true, mapperClass, modelClass, tableInfo);
            this.injectSelectByQuery(true, false, mapperClass, modelClass, tableInfo);
            this.injectSelectByQuery(false, true, mapperClass, modelClass, tableInfo);
            this.injectSelectByQuery(false, false, mapperClass, modelClass, tableInfo);



            this.injectGetPage(true, mapperClass, modelClass, tableInfo);
            this.injectGetPage(false, mapperClass, modelClass, tableInfo);

            this.injectCountByQuery(mapperClass, tableInfo);

        }
        MybatisConfiguration.MAPPER_REGISTRY_CACHE.add(className);
    }



    public void injectResultMap(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        String className = mapperClass.getName();
        if (MybatisConfiguration.RESULTMAP_REGISTRY_CACHE.contains(className)) {
            return;
        }
        this.configuration = configuration;
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanuageInstance();
        Class<?> modelClass = extractModelClass(mapperClass);
        TableInfo tableInfo = null;
        try {
            tableInfo = TableInfoUtil.init(modelClass);
        } catch (IWMybatisException e) {
            e.printStackTrace();
        }
        this.buildResultMappingFromContext(tableInfo, modelClass);
        MybatisConfiguration.RESULTMAP_REGISTRY_CACHE.add(className);
    }

    private Class<?> extractModelClass(Class<?> mapperClass) {
        Type[] types = mapperClass.getGenericInterfaces();
        ParameterizedType target = null;
        for (Type type : types) {
            if (type instanceof ParameterizedType && BaseMapper.class.isAssignableFrom(mapperClass)) {
                target = (ParameterizedType) type;
                break;
            }
        }
        Type[] parameters = target.getActualTypeArguments();
        Class<?> modelClass = (Class<?>) parameters[0];
        return modelClass;
    }

    /**
     * 插入
     *
     * @param isSelective
     * @param mapperClass
     * @param modelClass
     * @param tableInfo
     */
    private void injectInsert(boolean isBatch, boolean isSelective, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new Jdbc3KeyGenerator();
        StringBuffer fieldBuilder = new StringBuffer();
        StringBuffer placeholderBuilder = new StringBuffer();
        fieldBuilder.append("\n<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n");
        placeholderBuilder.append("\n<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n");
        SqlMethod sqlMethod = (!isSelective ? (isBatch ? SqlMethod.BATCH_INSERT : SqlMethod.INSERT) : SqlMethod.INSERT_SELECTIVE);
        List<ColumnInfo> columnInfoList = tableInfo.getColumnInfoList();
        for (ColumnInfo columnInfo : columnInfoList) {
            //跳过主键ID,不插入
            if (columnInfo == tableInfo.getPrimary()) {
                continue;
            }
            if (isSelective) {
                //selective没有批量插入
                fieldBuilder.append("<if test=\"" + columnInfo.getProperty() + " != null\">\n");
                fieldBuilder.append(columnInfo.getName()+",\n");
                placeholderBuilder.append("<if test=\"" + columnInfo.getProperty() + " != null\">\n");
                placeholderBuilder.append(buildColumnInfo(columnInfo) + ",");
                fieldBuilder.append("</if>\n");
                placeholderBuilder.append("</if>\n");
            } else {
                fieldBuilder.append(columnInfo.getName() + ",\n");
                //如果是批量插入，则需要批量
                if (isBatch) {
                    //批量插入有前缀
                    placeholderBuilder.append(buildColumnInfo(columnInfo, "item") + ",");
                } else {
                    placeholderBuilder.append(buildColumnInfo(columnInfo) + ",");
                }
            }
        }
        fieldBuilder.append("</trim>\n");
        placeholderBuilder.append("</trim>\n");
        String sql = String.format(sqlMethod.getSql(), tableInfo.getName(), fieldBuilder.toString(),
                placeholderBuilder.toString());
        logger.debug("inject sql:{}", sql);
        SqlSource sqlSource = this.languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, tableInfo.getPrimary().getProperty(),
                tableInfo.getPrimary().getName());
    }

    /**
     * 通过主键删除
     *
     * @param batch
     * @param mapperClass
     * @param tableInfo
     */
    private void injectDeleteByPrimaryKey(boolean batch, Class<?> mapperClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = batch ? SqlMethod.DELETE_BY_PRIMARY_KEYS : SqlMethod.DELETE_BY_PRIMARY_KEY;
        String sql;
        SqlSource sqlSource;
        String method = sqlMethod.getMethod();
        if (batch) {
            StringBuffer ids = new StringBuffer();
            ids.append("\n<foreach item=\"item\" index=\"index\" collection=\"list\" separator=\",\">");
            ids.append("#{item}");
            ids.append("\n</foreach>");
            sql = String.format(sqlMethod.getSql(), tableInfo.getName(), tableInfo.getPrimary().getName(), ids.toString());
            sqlSource = this.languageDriver.createSqlSource(configuration, sql, List.class);
        } else {
            sql = String.format(sqlMethod.getSql(), tableInfo.getName(), tableInfo.getPrimary().getName(), buildColumnInfo(tableInfo.getPrimary()));
            sqlSource = new RawSqlSource(configuration, sql, Object.class);
        }
        logger.debug("inject sql:{}", sql);
        this.addDeleteMappedStatement(mapperClass, method, sqlSource);
    }

    /**
     * 通过查询条件删除
     *
     * @param mapperClass
     * @param tableInfo
     */
    private void injectDeleteByQuery(Class<?> mapperClass, TableInfo tableInfo) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("<if test=\"_parameter != null\" >");
        stringBuffer.append(String.format(WHERE_CLAUSE, ""));
        stringBuffer.append("</if>");
        String sql = String.format(SqlMethod.DELETE_BY_QUERY.getSql(), tableInfo.getName(), stringBuffer.toString());
        logger.debug("inject sql:{}", sql);
        SqlSource sqlSource = this.languageDriver.createSqlSource(configuration, sql, Query.class);
        this.addDeleteMappedStatement(mapperClass, SqlMethod.DELETE_BY_QUERY.getMethod(), sqlSource);

    }

    /**
     * 通过主键ID更新(是否乐观，是否去空)
     *
     * @param isOptimistic
     * @param isSelective
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectUpdateByPrimaryKey(boolean isOptimistic, boolean isSelective, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod;
        if (isSelective) {
            //如果是selective更新，判断是否是乐观锁
            sqlMethod = (isOptimistic == Boolean.FALSE ? SqlMethod.UPDATE_BY_PRIMARY_KEY_SELECTIVE_NO_OPTIMISTIC : SqlMethod.UPDATE_BY_PRIMARY_KEY_SELECTIVE);
        } else {
            sqlMethod = (isOptimistic == Boolean.FALSE ? SqlMethod.UPDATE_BY_PRIMARY_KEY_NO_OPTIMISTIC : SqlMethod.UPDATE_BY_PRIMARY_KEY);
        }
        //乐观锁支持
        String optimisticInfo = "";
        //如果开启乐观锁
        if (isOptimistic) {
            //判断是否有乐观锁字段
            optimisticInfo = buildColumnInfo(table.getOptimistic());
            if (!StringUtils.isEmpty(optimisticInfo)) {
                optimisticInfo = " AND " + table.getOptimistic().getName() + " = " + optimisticInfo;
            }
        }
        String sql = String.format(sqlMethod.getSql(), table.getName(), sqlSet(isSelective, table, isOptimistic), table.getPrimary().getName(),
                buildColumnInfo(table.getPrimary()), optimisticInfo);
        logger.debug("inject sql:{}", sql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * 通过查询语句更新
     *
     * @param selective
     * @param mapperClass
     * @param modelClass
     * @param table
     */
    protected void injectUpdateByQuery(boolean selective, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
        SqlMethod sqlMethod = (selective == Boolean.TRUE ? SqlMethod.UPDATE_BY_QUERY_SELECTIVE : SqlMethod.UPDATE_BY_QUERY);
        StringBuffer whereCondition = new StringBuffer();
        whereCondition.append("<if test=\"" + QUERY_ENTITY + " != null\" >");
        whereCondition.append(String.format(WHERE_CLAUSE, QUERY_ENTITY + "."));
        whereCondition.append("</if>");
        String sql = String.format(sqlMethod.getSql(), table.getName(), sqlSet(selective, table, ENTITY, false), whereCondition.toString());
        logger.debug("inject sql:{}", sql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Map.class);
        this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
    }

    /**
     * 通过主键查询
     *
     * @param batch
     * @param mapperClass
     * @param modelClass
     * @param tableInfo
     */
    private void injectSelectByPrimaryKey(boolean isAssignReturnFields, boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql;
        SqlSource sqlSource;
        SqlMethod sqlMethod = null;
        if (batch) {
            StringBuffer ids = new StringBuffer();
            ids.append("\n<foreach item=\"item\" index=\"index\" collection=\"" + PRIMARY_KEYS + "\" separator=\",\">");
            ids.append("#{item}");
            ids.append("\n</foreach>");
            sqlMethod = isAssignReturnFields ? SqlMethod.SELECT_BY_PRIMARY_KEYS_SPECIFIED_COLUMN : SqlMethod.SELECT_BY_PRIMARY_KEYS;
            sql = String.format(sqlMethod.getSql(), baseColumnList(tableInfo, isAssignReturnFields), tableInfo.getName(), tableInfo.getPrimary().getName(), ids.toString());
        } else {
            sqlMethod = isAssignReturnFields ? SqlMethod.SELECT_BY_PRIMARY_KEY_SPECIFIED_COLUMN : SqlMethod.SELECT_BY_PRIMARY_KEY;
            sql = String.format(sqlMethod.getSql(), baseColumnList(tableInfo, isAssignReturnFields), tableInfo.getName(), tableInfo.getPrimary().getName(),
                    buildColumnInfoByAssignKey(tableInfo.getPrimary(), ISqlInjector.PRIMARY_KEY));
        }
        sqlSource = this.languageDriver.createSqlSource(configuration, sql, Map.class);
        logger.debug("inject sql:{}", sql);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo);

    }

    /**
     * 通过查询条件获取
     *
     * @param isAssignReturnFields 是否指定返回字段
     * @param isLimitOne
     * @param mapperClass
     * @param modelClass
     * @param tableInfo
     */
    private void injectSelectByQuery(boolean isAssignReturnFields, boolean isLimitOne, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = null;
        if (isLimitOne) {
            sqlMethod = isAssignReturnFields ? SqlMethod.SELECT_ONE_BY_QUERY_SPECIFIED_COLUMN : SqlMethod.SELECT_ONE_BY_QUERY;
        } else {
            sqlMethod = isAssignReturnFields ? SqlMethod.SELECT_BY_QUERY_SPECIFIED_COLUMN : SqlMethod.SELECT_BY_QUERY;
        }
        String sql = String.format(sqlMethod.getSql(),
                //distinct构建
                String.format(DISTINCT_TEMPLATE, ISqlInjector.QUERY_ENTITY + ".", baseColumnList(tableInfo, isAssignReturnFields)),
                tableInfo.getName(),
                //where语句构建
                String.format(WHERE_CLAUSE, ISqlInjector.QUERY_ENTITY + "."),
                //order语句构建
                String.format(ORDER_TEMPLATE, ISqlInjector.QUERY_ENTITY + ".", ISqlInjector.QUERY_ENTITY + ".")

        );
        SqlSource sqlSource = this.languageDriver.createSqlSource(configuration, sql, Map.class);
        logger.debug("inject sql:{}", sql);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo);
    }


    private void injectGetPage(boolean isAssignReturnFields, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = isAssignReturnFields ? SqlMethod.GET_PAGE_SPECIFIED_COLUMN : SqlMethod.GET_PAGE;
        String sql = String.format(sqlMethod.getSql(),
                //distinct构建
                String.format(DISTINCT_TEMPLATE, ISqlInjector.QUERY_ENTITY + ".", baseColumnList(tableInfo, isAssignReturnFields)),
                tableInfo.getName(),
                //where语句构建
                String.format(WHERE_CLAUSE, ISqlInjector.QUERY_ENTITY + "."),
                //order语句构建
                String.format(ORDER_TEMPLATE, ISqlInjector.QUERY_ENTITY + ".", ISqlInjector.QUERY_ENTITY + ".")

        );
        SqlSource sqlSource = this.languageDriver.createSqlSource(configuration, sql, Map.class);
        logger.debug("inject sql:{}", sql);
        this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo);
    }

    /**
     * 通过查询条件获取总条数
     *
     * @param mapperClass
     * @param tableInfo
     */
    private void injectCountByQuery(Class<?> mapperClass, TableInfo tableInfo) {
        String sql = String.format(SqlMethod.COUNT_BY_QUERY.getSql(), tableInfo.getName(), String.format(WHERE_CLAUSE, ISqlInjector.QUERY_ENTITY + "."));
        SqlSource sqlSource = this.languageDriver.createSqlSource(configuration, sql, Map.class);
        logger.debug("inject sql:{}", sql);
        this.addSelectMappedStatement(mapperClass, SqlMethod.COUNT_BY_QUERY.getMethod(), sqlSource, Integer.class, tableInfo);
    }


    /**
     * 创建基本返回字段内容
     *
     * @param tableInfo
     * @return
     */
    private String baseColumnList(TableInfo tableInfo, Boolean isAssignReturnFields) {
        StringBuffer select = new StringBuffer();
        //TODO 返回字段为空数组处理
        if (isAssignReturnFields) {
            select.append("\n<choose>\n");
            select.append("\t<when test=\"" + RETURN_FIELDS_KEY + " != null\">\n");
            select.append("\t\t<foreach collection=\"" + RETURN_FIELDS_KEY + "\" index=\"index\" item=\"item\" separator=\",\">\n");
            select.append("\t\t\t${item}\n");
            select.append("\t\t</foreach>\n");
            select.append("\t</when>\n");
            select.append("\t<otherwise>\n");
            select.append("\t\t*\n");
            select.append("\t</otherwise>\n");
            select.append("</choose>\n");
            return select.toString();
        }

        for (ColumnInfo columnInfo : tableInfo.getColumnInfoList()) {
            select.append(columnInfo.getName() + ",");
        }
        String base = select.toString();
        if (base.endsWith(",")) {
            return base.substring(0, base.length() - 1);
        }
        return base;
    }


    /**
     * 生成set语法
     *
     * @param selective
     * @param table
     * @return
     */
    private String sqlSet(boolean selective, TableInfo table, boolean isOptimistic) {
        return sqlSet(selective, table, "", isOptimistic);
    }

    /**
     * 带前缀
     *
     * @param selective
     * @param table
     * @param prefix
     * @return
     */
    private String sqlSet(boolean selective, TableInfo table, String prefix, boolean isOptimistic) {
        String prefixStr = StringUtils.isEmpty(prefix) ? "" : prefix + ".";
        List<ColumnInfo> columnInfoList = new ArrayList<ColumnInfo>(table.getColumnInfoList());
        columnInfoList.remove(table.getPrimary());
        StringBuilder set = new StringBuilder();
        set.append("<trim prefix=\"SET\" suffixOverrides=\",\">");
        ColumnInfo optimistic = table.getOptimistic();
        if (isOptimistic && optimistic != null) {
            columnInfoList.remove(optimistic);
        }
        for (ColumnInfo columnInfo : columnInfoList) {
            if (selective) {
                set.append("<if test=\"" + prefixStr + columnInfo.getProperty() + " != null\">\n");
            }
            set.append(columnInfo.getName() + "=" + buildColumnInfo(columnInfo, prefix) + ",");
            if (selective) {
                set.append("</if>");
            }
        }
        //乐观锁更新
        if (isOptimistic && optimistic != null) {
            //如果不是自动更新时间的，那就认为是数字自增长型
            if (!isTimeType(optimistic)) {
                set.append(optimistic.getName() + "=" + optimistic.getName() + "+" + 1 + ",");
            }
        }
        set.append("</trim>");
        return set.toString();
    }


    private boolean isTimeType(ColumnInfo columnInfo) {
        String jdbcType = columnInfo.getJdbcType();
        if (!StringUtils.isEmpty(jdbcType) && (jdbcType.toLowerCase().contains("time") || jdbcType.toLowerCase().contains("date"))) {
            return true;
        }
        if (columnInfo.getJavaType().equals(Date.class)) {
            return true;
        }
        return false;
    }


    /**
     * #{switcher,jdbcType=BIT}
     *
     * @param columnInfo
     * @return
     */
    private String buildColumnInfo(ColumnInfo columnInfo) {
        return this.buildColumnInfo(columnInfo, "");
    }

    private String buildColumnInfoByAssignKey(ColumnInfo columnInfo, String key) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("#{" + key);
        if (!StringUtils.isEmpty(columnInfo.getJdbcType())) {
            stringBuffer.append(",jdbcType=" + MysqlType2JdbcType.get(columnInfo.getJdbcType()).name() + "");
        }
        stringBuffer.append("}");
        return stringBuffer.toString();
    }

    /**
     * 构建 #{switcher,jdbcType=BIT}
     *
     * @param columnInfo
     * @param prefix
     * @return
     */
    private String buildColumnInfo(ColumnInfo columnInfo, String prefix) {
        if (columnInfo == null) {
            return "";
        }
        if (StringUtils.isEmpty(prefix)) {
            prefix = "";
        } else {
            prefix = prefix + ".";
        }
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("#{" + prefix + columnInfo.getProperty());
        if (!StringUtils.isEmpty(columnInfo.getJdbcType())) {
            stringBuffer.append(",jdbcType=" + MysqlType2JdbcType.get(columnInfo.getJdbcType()).name() + "");
        }
        stringBuffer.append("}");
        return stringBuffer.toString();
    }


    /*
    * 增
    */
    public MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> modelClass, String id, SqlSource sqlSource,
                                                    KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, modelClass, null, Integer.class,
                keyGenerator, keyProperty, keyColumn);
    }

    /*
     * 删
     */
    public MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class,
                new NoKeyGenerator(), null, null);
    }

    /*
     * 改
	 */
    public MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> modelClass, String id, SqlSource sqlSource) {
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, modelClass, null, Integer.class,
                new NoKeyGenerator(), null, null);
    }

    /*
     * 查询
     */
    public MappedStatement addSelectMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource, Class<?> resultType,
                                                    TableInfo table) {
        if (null != table && resultType.getSimpleName().equals(table.getModelName())) {
            String resultMap = table.getResultMap();
            if (null != resultMap) {
                /* 返回 resultMap 映射结果集 */
                return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, resultMap, null,
                        new NoKeyGenerator(), null, null);
            }
        }

		/* 普通查询 */
        return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, null, resultType,
                new NoKeyGenerator(), null, null);
    }

    public MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,
                                              SqlCommandType sqlCommandType, Class<?> parameterClass, String resultMap, Class<?> resultType,
                                              KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
        String statementName = mapperClass.getName() + "." + id;
        if (configuration.hasStatement(statementName)) {
            System.err.println("{" + statementName
                    + "} Has been loaded by XML or SqlProvider, ignoring the injection of the SQL.");
            return null;
        }

        /* 缓存逻辑处理 */
        boolean isSelect = false;
        if (sqlCommandType == SqlCommandType.SELECT) {
            isSelect = true;
        }
        return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null,
                parameterClass, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
                configuration.getDatabaseId(), languageDriver, null);
    }


    private void buildResultMappingFromContext(TableInfo tableInfo, Class<?> resultType) {
        List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
        List<ColumnInfo> columnInfos = new ArrayList<ColumnInfo>(tableInfo.getColumnInfoList());
        for (ColumnInfo columnInfo : columnInfos) {
            String property = columnInfo.getProperty();
            String column = columnInfo.getName();
            String jdbcType = columnInfo.getJdbcType();
            Class<?> javaTypeClass = columnInfo.getJavaType();
            Class<? extends TypeHandler<?>> typeHandlerClass = columnInfo.getTypeHandler();
            List<ResultFlag> flags = new ArrayList<ResultFlag>();
            if (columnInfo.equals(tableInfo.getPrimary())) {
                flags.add(ResultFlag.ID);
            }
            JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
            resultMappings.add(builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, null, null, null, null, typeHandlerClass, flags, null, null, false));

        }
        ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, DEFAULT_RESULT_MAP, resultType, null, null, resultMappings, true);
        try {
            resultMapResolver.resolve();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteResultMap(resultMapResolver);
            throw e;
        }

    }


    protected JdbcType resolveJdbcType(String alias) {
        if (alias == null) {
            return null;
        }
        return MysqlType2JdbcType.get(alias);
    }


}
